﻿using System;
using System.Collections.Generic;
using System.Web;
using Sherwood.Caching;
using Sherwood.SignOn;
using Sherwood.SignOn.Client;
using System.Linq;
using Sherwood.Web;

namespace Sherwood.Content.SearchServices
{
    public class SignOnUserAuthenticator : SignedClientAuthenticator
    {
        private readonly SignOnServicesClient _signOnServicesClient;
        private readonly IRolesProvider _rolesProvider;
        private readonly LRUCache<SessionRoles> _rolesCache;
        private readonly LRUCache<SessionRoles>.IIndex<Guid> _rolesIndex;

        public SignOnUserAuthenticator(SignOnServicesClient signOnServicesClient, IRolesProvider rolesProvider, IClientRepository clientRepository) 
            : base(clientRepository)
        {
            _signOnServicesClient = signOnServicesClient;
            _rolesProvider = rolesProvider;
            _rolesCache = new LRUCache<SessionRoles>(100, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(60), null);
            _rolesIndex = _rolesCache.AddIndex("BySessionId", item => item.SessionId, LoadItem);
        }
        
        public override bool Authenticates(QueryParameterCollection queryString)
        {
            var fq = "roles:\"Public\"";
            QueryParameter sessionId;
            if (queryString.TryGetValue("session", out sessionId))
            {
                var roles = _rolesIndex[sessionId.ValueAsGuid];
                foreach (var role in roles.Roles)
                {
                    if (_rolesProvider.Exists(role))
                        fq += " OR roles:\"" + role + "\"";
                }
            }
            var authenticates = base.Authenticates(queryString);
            if (sessionId != null)
                queryString.Remove(sessionId);
            queryString.Add("fq", fq);
            return authenticates;
        }

        private SessionRoles LoadItem(Guid sessionId)
        {
            var profile = _signOnServicesClient.GetUserProfile(sessionId);
            if(profile == null)
                throw new Exception("No profile returned for session " + sessionId);
            var roles = profile.role.Select(r => r.source + ":" + r.name).ToList();
            // to (potentially) allow better caching in solr, we sort the roles
            roles.Sort();
            return new SessionRoles(sessionId, roles);
        }

        private class SessionRoles
        {
            public SessionRoles(Guid sessionId, List<string> roles)
            {
                SessionId = sessionId;
                Roles = roles;
            }

            public Guid SessionId { get; private set; }
            public List<string> Roles { get; private set; }
        }
    }
}